<html lang="en-US">

<head>
    
<title>限流熔断是什么，怎么做，不做行不行？ - 花落雨忧</title>

<meta property="og:title" content="限流熔断是什么，怎么做，不做行不行？ - 花落雨忧">



    



    
    <meta property="description" content="“在分布式应用中，最常见的问题是什么呢？”
“一个分布式应用部署上去后，还要关注什么？”
“这服务的远程调用依赖似乎有点多&amp;hellip;”
[&amp;hellip;] 在 《微服务的战争：级联故障和雪崩》中有提到，在一个分布式应用中，最常见，最有危险性之一的点就是级联故障所造成的雪崩，而其对应的解决方案为根据特定的规则/规律进行流量控制和熔断降级，避免请求发生堆积，保护自身应用，也防止服务提供方进一步 &amp;hellip;">
    <meta property="og:description" content="“在分布式应用中，最常见的问题是什么呢？”
“一个分布式应用部署上去后，还要关注什么？”
“这服务的远程调用依赖似乎有点多&amp;hellip;”
[&amp;hellip;] 在 《微服务的战争：级联故障和雪崩》中有提到，在一个分布式应用中，最常见，最有危险性之一的点就是级联故障所造成的雪崩，而其对应的解决方案为根据特定的规则/规律进行流量控制和熔断降级，避免请求发生堆积，保护自身应用，也防止服务提供方进一步 &amp;hellip;">
    






<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1">

<link rel="shortcut icon" href="https://www.lican.site/logo/logo.png" type="image/x-icon" />



<link rel="stylesheet" href="/css/style.min.css" />

<link rel="stylesheet" href="/css/reset.min.css" />




<script src="https://www.lican.site/js/highlight.min.js"></script>

<script>
hljs.configure({ ignoreUnescapedHTML: true })
hljs.highlightAll();
</script>


<script src="https://www.lican.site/js/jquery.min.js"></script>




<link href="https://www.lican.site/css/hugo-code.min.css" rel="stylesheet" />



    <style>
        .post-content img {
            max-width: 400px;
        }
    </style>
</head>

<body id="period" class="home blog">
    <a class="skip-content" href="#main">Press "Enter" to skip to content</a>
    <div id="overflow-container" class="overflow-container">
        <header class="site-header" id="site-header" role="banner">
    <div class="max-width">
        <div id="title-container" class="title-container">
            <div id="site-title" class="site-title"><a href="/">花落雨忧</a></div>
            <p class="tagline">思所及 力所行 方为真.</p>
        </div>
        
        <div id="menu-primary-container" class="menu-primary-container">
            <div class="icon-container">
            </div>
            <div id="menu-primary" class="menu-container menu-primary" role="navigation">
                <nav class="menu">
                    <ul id="menu-primary-items" class="menu-primary-items">
                        
                        
                        <li id="menu-item-0"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/posts" aria-current="page" tabindex="0">首页</a></li>
                        
                        <li id="menu-item-1"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/tech/" aria-current="page" tabindex="1">技术文档</a></li>
                        
                        <li id="menu-item-2"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/article/" aria-current="page" tabindex="2">文章</a></li>
                        
                        <li id="menu-item-3"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/project/" aria-current="page" tabindex="3">项目</a></li>
                        
                        <li id="menu-item-4"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/about/" aria-current="page" tabindex="4">关于</a></li>
                        
                    </ul>
                </nav>
            </div>
        </div>
    </div>
</header>
        <div id="primary-container" class="primary-container">
            <div class="max-width">
                <section id="main" class="main" role="main">
                    <div id="loop-container" class="loop-container">
                        <div
                            class="post type-post status-publish format-standard hentry entry">
                            <article>
                                <div class="post-container">
                                    <div class="post-header">
                                        <h2 class="post-title">
                                            <a href="/posts/microservice/flowcontrol-circuitbreaker/">限流熔断是什么，怎么做，不做行不行？</a>
                                        </h2>
                                        
                                        <div class="post-byline">Published on
                                            <a class="date" href="javascript:;">2020/10/05</a>
                                            
                                            
                                            
                                            
                                            
                                        
                                    </div>
                                    <div class="post-content">
                                        <p>“在分布式应用中，最常见的问题是什么呢？”</p>
<p>“一个分布式应用部署上去后，还要关注什么？”</p>
<p><img src="https://image.eddycjy.com/8c42e87dd4f7f1c59431c6185f608699.png" alt="image"></p>
<p>“这服务的远程调用依赖似乎有点多&hellip;”</p>
<h2 id="前言">前言</h2>
<p>在 <a href="https://eddycjy.com/posts/microservice/linkage/">《微服务的战争：级联故障和雪崩》</a>中有提到，在一个分布式应用中，最常见，最有危险性之一的点就是级联故障所造成的雪崩，而其对应的解决方案为<strong>根据特定的规则/规律进行流量控制和熔断降级</strong>，避免请求发生堆积，保护自身应用，也防止服务提供方进一步过载。</p>
<p><img src="https://image.eddycjy.com/79a7f8870edffd331432272cf5db2c46.jpg" alt="image"></p>
<p>简单来讲就是，要控制访问量的流量，要防各类调用的强/弱依赖，才能保护好应用程序的底线。</p>
<h2 id="诉求期望">诉求，期望</h2>
<ol>
<li>
<p>诉求：作为一个业务，肯定是希望自家的应用程序，能够全年无休，最低限度也要有个 4 个 9，一出突发性大流量，在资源贫乏的窗口期，就马上能够自动恢复。</p>
</li>
<li>
<p>期望：万丈高楼平地起，我们需要对应用程序进行流量控制、熔断降级。确保在特定的规则下，系统能够进行容错，只处理自己力所能及的请求。若有更一步诉求，再自动扩缩容，提高系统资源上限。</p>
</li>
</ol>
<h2 id="解决方案">解决方案</h2>
<p>要如何解决这个问题呢，可以关注到问题的核心点是 “系统没有任何的保护的情况下”，因此核心就是让系统，让你的应用程序有流量控制的保护。一般含以下几个方面：</p>
<ul>
<li>
<p>来自端控制：在业务/流量网关处内置流量控制、熔断降级的外部插件，起到端控制的效果。</p>
</li>
<li>
<p>来自集群/节点控制：在统一框架中内建流量控制、熔断降级的处理逻辑，起到集群/节点控制的效果。</p>
</li>
<li>
<p>来自 Mesh 控制：通过 ServiceMesh 来实现流量控制、熔断降级。侵入性小，能带来多种控制模式，但有利有弊。</p>
</li>
</ul>
<p>以上的多种方式均可与内部的治理平台打通，且流量控制、熔断降级是不止面试应用程序的，就看资源埋点上如何设计、注入。常见有如下几种角度：</p>
<ul>
<li>
<p>资源的调用关系：例如远程调用，像是面向 HTTP、SQL、Redis、RPC 等调用均，另外针对文件句柄控制也可以。</p>
</li>
<li>
<p>运行指标：例如 QPS、线程池、系统负载等。</p>
</li>
</ul>
<h2 id="流量控制">流量控制</h2>
<p>在资源不变的情况下，系统所能提供的处理能力是有限的。而系统所面对的请求所到来的时间和量级往往是随机且不可控的。因此就会存在可能出现突发性流量，而在系统没有任何的保护的情况下，系统就会在数分钟内无法提供正常服务，常见过程为先是出现调用延迟，接着持续出现饱和度上升，最终假死。</p>
<p><img src="https://image.eddycjy.com/6a58406bb7c90355c82c5cb50f417f9a.jpg" alt="image"></p>
<p>流量控制一般常见的有两种方式，分别是：基于 QPS、基于并发隔离。</p>
<h3 id="基于-qps">基于 QPS</h3>
<p>最常用的流量控制场景，就是基于 QPS 来做流控，在一定的时间窗口内按照特定的规则达到所设定的阈值则进行调控：</p>
<p><img src="https://image.eddycjy.com/58f0d6fe0043963f6e40c1d73c8e019e.jpg" alt="image"></p>
<h4 id="案例">案例</h4>
<p>在本文中借助 <a href="https://github.com/alibaba/sentinel-golang">sentinel-golang</a> 来实现案例所需的诉求，代码如下：</p>
<pre><code>import (
	...
	sentinel &quot;github.com/alibaba/sentinel-golang/api&quot;
	&quot;github.com/alibaba/sentinel-golang/core/base&quot;
	&quot;github.com/alibaba/sentinel-golang/core/flow&quot;
	&quot;github.com/alibaba/sentinel-golang/util&quot;
)

func main() {
	_ = sentinel.InitDefault()
	_, _ = flow.LoadRules([]*flow.Rule{
		{
			Resource:               &quot;控制吃煎鱼的速度&quot;,
			Threshold:              60,
			ControlBehavior:        flow.Reject,
		},
	})

	...
	e, b := sentinel.Entry(&quot;控制吃煎鱼的速度&quot;, sentinel.WithTrafficType(base.Inbound))
	if b != nil {
	    // Blocked
	} else {
	    // Passed
	    e.Exit()
	}
}
</code></pre><p>总的来讲，上述规则结果就是 1s 内允许通过 60 个请求，超出的请求的处理策略为直接拒绝（Reject）。</p>
<p>首先我们初始化了 Sentinel 并定义资源（Resource）为 “控制吃煎鱼的速度”。其 Threshold 配置为 3，也就是 QPS 的阈值为 3，统计窗口未设置默认值为 1s，ControlBehavior 控制的行为为直接拒绝。</p>
<p>而在满足阈值条件后，常见的处理策略还有匀速排队（Throttling），匀速排队方式会严格控制请求通过的间隔时间，也就是让请求以均匀的速度通过。</p>
<h3 id="基于并发隔离">基于并发隔离</h3>
<p>基于资源访问的并发协程数来控制对资源的访问数量，主要是控制对资源访问的最大协程数，避免因为资源的异常导致协程耗尽。</p>
<p><img src="https://image.eddycjy.com/f255b874409c8a894b7254558b0828ca.jpg" alt="image"></p>
<p>这类情况，Go 语言在设计上常常可以使用协程池来进行控制，但设计总是赶不上计划的，且不同场景情况可能不同，因此作为一个日常功能也是非常有存在的必要性。</p>
<h2 id="熔断降级">熔断降级</h2>
<p>在分布式应用中，随着不断地业务拆分，远程调用逐渐变得越来越多。且在微服务盛兴的情况下，一个小业务拆出七八个服务的也常有。</p>
<p>此时就会出现一个经典的问题，那就是客户端的一个普通调用，很有可能就要经过好几个服务，而一个服务又有可能远程调用外部 HTTP、SQL、Redis、RPC 等，调用链会特别的长。</p>
<p>若其中一个调用流程出现了问题，且没有进行调控，就会出现级联故障，最终导致系统雪崩：</p>
<p><img src="https://image.eddycjy.com/2e7b0d94c32f3bd8f3f7168046671d2b.jpg" alt="image"></p>
<p>服务 D 所依赖的外部接口出现了故障，而他并没有做任何的控制，因此扩散到了所有调用到他的服务，自然也就包含服务 B，因此最终出现系统雪崩。</p>
<p>这种最经典的是出现在默认 Go http client 调用没有设置 Timeout，从而只要出现一次故障，就足矣让记住这类 “坑”，毕竟崩的 ”慢“，错误日志还多。（via: 《微服务的战争：级联故障和雪崩》）</p>
<h3 id="目的和措施">目的和措施</h3>
<p>为了解决上述问题所带来的灾难，在分布式应用中常需要对服务依赖进行熔断降级。在存在问题时，暂时切断内部调用，避免局部不稳定因素导致整个分布式系统的雪崩。</p>
<p>而熔断降级作为保护服务自身的手段，通常是在客户端进行规则配置和熔断识别：</p>
<p><img src="https://image.eddycjy.com/0fc0b2f9367d384d4ef0a6398054a8c6.jpg" alt="image"></p>
<p>常见的有三种熔断降级措施：慢调用比例策略、错误比例策略、错误计数策略。</p>
<h3 id="慢调用比例">慢调用比例</h3>
<p>在所设定的时间窗口内，慢调用的比例大于所设置的阈值，则对接下来访问的请求进行自动熔断。</p>
<h3 id="错误比例">错误比例</h3>
<p>在所设定的时间窗口内，调用的访问错误比例大于所设置的阈值，则对接下来访问的请求进行自动熔断。</p>
<h3 id="错误计数">错误计数</h3>
<p>在所设定的时间窗口内，调用的访问错误次数大于所设置的阈值，则对接下来访问的请求进行自动熔断。</p>
<h2 id="实践案例">实践案例</h2>
<p>知道流量控制、熔断降级的基本概念和功能后，在现实环境中应该如何结合项目进行使用呢。最常见的场景是可针对业务服务的 HTTP 路由进行流量控制，以 HTTP 路由作为资源埋点，这样子就可以实现接口级的调控了。</p>
<p><img src="https://image.eddycjy.com/00c7676ca398bf4c5cc8725e69ce77b0.jpg" alt="image"></p>
<p>还可以增强其功能特性，针对参数也进行多重匹配。常会有这种限流诉求：针对 <code>HTTP GET /eddycjy/info</code> 且 language 为 go 的情况下进行限流。另外还可以针对 HTTP 调用封装统一方法，进行默认的熔断注入，实现多重保障。</p>
<p>而结合系统负载、服务 QPS 等，可以对限流熔断的规则数据源进行实时调控，再结合 Watch 机制，就能够比较平滑的实现自适应限流熔断的接入。</p>
<h2 id="总结">总结</h2>
<p>在分布式应用中，限流熔断是非常重要的一环，越早开始做越有益处。但需要注意的是，不同公司的业务模型多多少少有些不一样，所针对的匹配维度多少有些不同，因此需要提前进行业务调研。</p>
<p>且在做业务的限流熔断时，注意把度量指标的打点做上，这样子后续就能够结合 Prometheus+Grafana+Alertmanager 做一系列的趋势图，熔断告警，自动扩缩容等相关工作了，会是一个很好的助力。</p>


                                        
                                        
                                        
                                        <div class="rp4wp-related-posts">
                                            <h3>相关文章</h3>
                                            <ul>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/microservice/monitor-alarm/">想要4个9？本文告诉你监控告警如何做</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/microservice/tests/">微服务的灾难：端到端测试的痛苦</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/microservice/tracing/">微服务的战争：选型？分布式链路追踪</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/microservice/linkage/">微服务的战争：级联故障和雪崩</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/microservice/standardization/">微服务的战争：统一且标准化</a>
                                                    </div>
                                                </li>
                                                
                                            </ul>
                                        </div>
                                        
                                        
                                    </div>

                                    
                                    
                                    

                                    
                                    <div class="post-meta">
                                        
                                        
                                        <div class="post-tags">
                                            <ul>
                                            
                                            <li>
                                                <a href="/tags/%e5%be%ae%e6%9c%8d%e5%8a%a1" title="View all posts tagged match">微服务</a>
                                            </li>
                                            
                                            <li>
                                                <a href="/tags/%e6%9c%8d%e5%8a%a1%e6%b2%bb%e7%90%86" title="View all posts tagged match">服务治理</a>
                                            </li>
                                            
                                            </ul>
                                        </div>
                                        
                                        
                                        <nav class="further-reading">
                                            
                                            <div class="previous">
                                                <span>&lt;&lt; Prev</span>
                                                <a href="https://www.lican.site/posts/microservice/monitor-alarm/"
                                                    rel="prev">想要4个9？本文告诉你监控告警如何做</a> </div>
                                            
                                            
                                            <div class="next">
                                                <span>Next >></span>
                                                <a href="https://www.lican.site/posts/microservice/leaky-token-buckets/">带你快速了解：限流中的漏桶和令牌桶算法</a> 
                                            </div>
                                            
                                        </nav>
                                    </div>
                                    
                                    

                                    
                                    
                                    

                                </div>
                            </article>
                        </div>
                    </div>
                </section>
                <aside class="sidebar sidebar-primary" id="sidebar-primary" role="complementary">
    <h1 class="screen-reader-text">Sidebar</h1>
    

    
    
    <section id="text-2" class="widget widget_text">
        <div class="textwidget">
            
            <div id="profile">
                <div id="profile_picture"><img src="https://www.lican.site/logo/logo.png"></div>
                <div id="profile_intro">
                    <p><span class="name">Lican</span></p>
                    <p class="intro">全栈开发者，爱好造轮子。</p>
                </div>
            </div>
            
            <p>
                <script type="text/javascript">
                    (function ($) {
                        $(document).ready(function () {
                            var menuPrimaryContainer = $('#menu-primary-container');
                            var profile = $('#text-2');
                            $('#toggle-navigation').click(function () {
                                if (menuPrimaryContainer.hasClass('open')) {
                                    profile.removeClass('open');
                                } else {
                                    profile.addClass('open');
                                }
                            });
                        });
                    })(jQuery);
                </script>
            </p>
        </div>
    </section>
    
    
    
    
    
    <section id="text-5" class="widget widget_text">
        <h2 class="widget-title">开源项目</h2>
        <div class="textwidget">
            <div id="projects" style="line-height: 22px;">
                
                <a href="https://github.com/idoubi/gonews"
                    target="_blank">gonews</a>: &nbsp;Daily news for golang<br>
                
                <a href="https://github.com/idoubi/sql2struct"
                    target="_blank">sql2struct</a>: &nbsp;Generate go struct according to SQL<br>
                
                <a href="https://github.com/idoubi/goz"
                    target="_blank">goz</a>: &nbsp;Request library used in golang<br>
                
        </div>
    </section>
    
    

    
    
    
    
    

    
    
    

    
    
    

    
    
    
    
</aside>
            </div>
        </div>

        <footer id="site-footer" class="site-footer" role="contentinfo">
    <div class="max-width">
    </div>
    <div class="footer">
        <div id="footercontent">
            © lican.vip All rights reserved<br/>
            Built with Hugo Theme <a href="https://github.com/idoubi/hugo-theme-period" target="_blank">Period</a>
        </div>
    </div>
</footer>

<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?e8351b6d4626d5881d439ea1f6184baa";
      var s = document.getElementsByTagName("script")[0]; 
      s.parentNode.insertBefore(hm, s);
    })();
</script>
    
    
    </div>
    
</body>

</html>